<?php

namespace app\controllers\pay;

use app\commission\PaidFinish;
use app\controllers\Controller;
use app\controllers\wx\services\Wx;
use app\models\pay\Pay;
use app\models\system\SetPay;
use app\models\system\SetWx;
use app\util\Debug;
use app\util\Request;
use app\util\Util;
use Exception;

class PayWxH5Controller extends Controller
{
    public $enableCsrfValidation = false;

    // 别的请求有问题, 我艹
    public function p($url, $params = [],  $method = 'post')
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_CERTINFO , true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);


        if ($method == 'post') {
            curl_setopt($curl, CURLOPT_POST, TRUE);
            if(is_array($params)){
                // 这里因为有多维数组, 要改成json
                $params = http_build_query($params);
                // $params = json_encode($params);
            }
            curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
        }else{
            curl_setopt($curl, CURLOPT_POST, FALSE);
        }

        // 请求微信, 要验证
        // 否则 Http头缺少Accept或User-Agent
        // curl_setopt($curl, CURLOPT_HTTPHEADER, array(
        //     'Authorization:'.$key,
        //     'Content-Type:application/json',
        //     'Accept:application/json',
        //     'User-Agent:'.$_SERVER['HTTP_USER_AGENT']
        // ));

        $res = curl_exec($curl);

        curl_close($curl);

        if ($res) {
            return $res;
        } else {
            // return curl_error($curl);
            // file_put_contents(__DIR__ . '/a.txt', curl_error($curl));
        }

        return $res;
    }

    private function unifiedOrder($sn, $body, $total_fee, $callback_url, $open_id, $timestamp, $noncestr)
    {
        // v3版的key, 后台是v2的
        $v3_key = 'CfCiBzCCBH2y7sxnt4H787XQk2mT6dkM';

        // var_dump($v3_key);exit;

        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        // v3 h5支付, 非公众号支付
        // $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/h5';

        // $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
        $wx = SetWx::getLastOne(false);

        $params = [
            'appid' => $wx->app_id,
            // 不知道哪里多了一个空格
            'mch_id' => trim($wx->mch_id),
            // 'mchid' => trim($wx->mch_id),
            'nonce_str' => $noncestr,
            'body' => $body,
            // 'description' => $body,
            // 调试的时候,订单号要不一样
            'out_trade_no' => $sn,
            'total_fee' => $total_fee,
            // 'amount' => [
            //     'total' => $total_fee,
            //     'currency' => 'CNY'
            // ],
            // 'total_fee' => 1,
            'spbill_create_ip' => Util::getIp(),
            // 'scene_info' => [
            //     'payer_client_ip' => Util::getIp(),
            //     'h5_info' => [
            //         'type' => 'Wap'
            //     ]
            // ],
            'notify_url' => $callback_url,
            'trade_type' => 'MWEB',
            // 这个不需要了
            // 'openid' => $open_id,
            // 这个参数不能传
            'timeStamp' => $timestamp,
        ];

        // $x = $this->p($url, $this->arrayToXml($params));
        // var_dump($x);exit;


        // 签名
        $params['sign'] = $this->getSign($params, trim($wx->pay_key));

        $xml_data = $this->arrayToXml($params);

        // 验证地址
        // 本地ip验证, 签名会出错,
        // 还要注意Openid对应
        // https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=20_1
        // var_dump($xml_data);exit;

        // 请求
        $result = $this->p($url, $xml_data);

        var_dump($result);exit;

        if ($result) {
            $result = $this->xmlToArray($result);
            if (isset($result['result_code']) && $result['result_code'] == 'SUCCESS') {
                return [
                    'params' => $params,
                    'prepay_id' => $result['prepay_id'],
                    // 'sign' => $result['sign']
                ];
            }
        }
    }

    public function actionPrepare()
    {
        // $set = SetPay::getLastOne();
        $wx = SetWx::getLastOne(false);

        // var_dump(strlen($wx->pay_key));exit;

        // order or refill
        $pay_type       = $this->post('pay_type', 1);
        $pay_type_text  = $this->post('pay_type_text', '订单');

        $open_id    = $this->post('openid');
        $sn         = $this->post('sn');
        // $amount     = $this->post('amount');
        // $amount     = round($amount, 2);
        $amount     = intval($this->post('amount') * 100);
        $body       = $this->post('body', '购买商品');

        $user_id    = $this->post('user_id');

        // wx signature
        // js => window.location.href
        $signature_url = $this->post('signature_url');

        $notify_url = \Yii::$app->request->getHostInfo() . '/pay/pay-wx-h5/callback';

        $noncestr = Util::generateNonceString();
        $timestamp = time();

        try {
            $data = $this->unifiedOrder($sn, $body, $amount, $notify_url, $open_id, $timestamp, $noncestr);
            // var_dump($data);exit ;
            if (!$data) {
                throw new Exception('请求支付失败1~~');
            }

            // 创建支付订单
            // $this->createPayOrder($sn, $amount, $pay_type, $pay_type_text, $user_id);
            // 微信支付是分
            $this->createPayOrder($sn, $amount / 100, $pay_type, $pay_type_text, $user_id);

            // 返回给前端的第二次签名,一定要用appId大写,(发起的时候是小写)
            // 还有prepay_id要参与签名,要写在后端
            // 切记, 弄了一天了
            $package = "prepay_id={$data['prepay_id']}";
            $s = [
                'appId' => $wx->app_id,
                'timeStamp' => $timestamp,
                'nonceStr' => $noncestr,
                'package' => $package,
                'signType' => 'MD5',
            ];

            $ticket = Wx::getJsapiTicket();
            $string = "jsapi_ticket={$ticket}&noncestr={$noncestr}&timestamp={$timestamp}&url={$signature_url}";
            $signature =  sha1($string);

            return $this->success([
                'appId' => $wx->app_id,
                'timestamp' => $timestamp,
                'nonceStr' => $noncestr,
                'signature' => $signature,
                'package' => $package,
                // 'sign' => $data['sign'],
                'sign' => $this->getSign($s, $wx->pay_key),
            ]);

        } catch (Exception $e) {
            Debug::pay('pay-fail', $e->getMessage());
            return $this->fail('请求支付失败22~');
        }
    }

    public function actionCallback()
    {
        $result = $this->checkCallback();
        if (!$result) {
            return $this->returnWx(false);
        }


        // 成功
        if (isset($result['result_code'])) {
            if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {

                $sn = $result['out_trade_no'];
                $amount = $result['total_fee'];
                $model = Pay::findOne(['sn' => $sn]);
                // if ($model && $model->amount == $amount/100) {
                if ($model) {
                    $model->status = 1;
                    $model->save();

                    // paid
                    PaidFinish::paidRun($model);
                }

                return $this->returnWx();
            }
        }
    }

    // create pay order
    protected function createPayOrder($sn, $amount, $pay_type, $pay_type_text, $user_id)
    {
        $model = new Pay();
        $model->sn = $sn;
        $model->amount = $amount;
        $model->user_id = $user_id;
        $model->status = 0;
        $model->pay_type = $pay_type;
        $model->pay_type_text = $pay_type_text;
        $model->pay_mode = '微信';
        $model->save();
    }

    // wx signature
    public function wxSignature($data, $url)
    {
        $ticket = Wx::getJsapiTicket();

        $string = "jsapi_ticket={$ticket}&noncestr={$data->nonceStr}&timestamp={$data->timeStamp}&url={$url}";

        return sha1($string);
    }

    protected function checkCallback()
    {
        $xml = file_get_contents('php://input');
        $data = $this->xmlToArray($xml);

        if($data['return_code'] == 'FAIL' || $data['result_code'] == 'FAIL') {
            return false;
        }

        /**
         * todo -1 验证签名
         */

        return $data;
    }

    protected function returnWx($flag = true)
    {
        $return_code = $flag ? 'SUCCESS' : 'FAIL';
        $return_msg = $flag ? 'OK' : 'FAIL';

        $xml = sprintf('<xml>
                  <return_code><![CDATA[%s]]></return_code>
                  <return_msg><![CDATA[%s]]></return_msg>
                </xml>', $return_code, $return_msg);

        return $xml;
    }

    public function xmlToArray($xml)
    {
        $array_data = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $array_data = json_encode($array_data);

        return json_decode($array_data, true);
    }

    public function arrayToXml($arr)
    {
        $xml = '';
        foreach ($arr as $k => $v) {
            if (is_numeric($v)) {
                $xml .= sprintf('<%s>%s</%s>', $k, $v, $k);
            } else {
                $xml .= sprintf('<%s><![CDATA[%s]]></%s>', $k, $v, $k);
            }
        }

        return sprintf('%s%s%s', '<xml>', $xml, '</xml>');
    }

    // 获取签名
    public function getSign($parameters, $key)
    {
        ksort($parameters);
        $string = $this->format($parameters);

        $string = $string . '&key=' . $key;
        $string = strtoupper(md5($string));

        return $string;
    }

    // 格式化参数，签名过程需要使用
    private function format($paramMap)
    {
        $buff = '';
        ksort($paramMap);
        foreach ($paramMap as $k => $v) {
            $buff .= $k . '=' . $v . '&';
        }

        if (strlen($buff) > 0) {
            return substr($buff, 0, strlen($buff) - 1);
        }
    }
}